/*
 * nwk.c
 *
 *  Created on: Jun 9, 2013
 *      Author: Jonathan
 */

#include <stdlib.h>
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "nwk.h"
#include "FreeRTOS.h"
#include "semphr.h"

/***** NWK/local vars *****/
nwk_packet_t nwk_send_queue[NWK_QUEUE_SIZE]; /* The circular fifo on which outgoing packets are stored */
uint8_t send_queue_in; /* index to queue head */
uint8_t send_queue_out; /* index to queue tail */

nwk_state_t nwk_tx_state;

xSemaphoreHandle tx_mutex;

/***** Local prototypes *****/
uint8_t 		nwk_tx_init(void);
static bool 	nwk_tx_queue_empty(void);
void 			nwk_transmit_pkt(void);
void 			nwk_tx_result_cb(bool completed);

extern void 	nwk_process_pkt(void); /* Located in nwk_rx.c, but internal to nwk layer */

uint8_t nwk_tx_init(void)
{
	/* initialize the queue indices */
	send_queue_in = send_queue_out = 0;

	/* clear the nwk packets */
	memset(nwk_send_queue, 0, (sizeof(nwk_packet_t) * NWK_QUEUE_SIZE));

	tx_mutex = xSemaphoreCreateMutex();
	if(tx_mutex == NULL)
		return NWK_FAILURE;

	//TODO2: this will have to clear routing tables

	/* Initialize the transceiver */
	nrf24_init();

	nwk_tx_state = NWK_IDLE;

	return NWK_SUCCESS;
}

static nwk_packet_t* nwk_tx_pkt_allocate(void)
{
	/* Make sure the send queue is not full */
	if((send_queue_in + 1) % NWK_QUEUE_SIZE == send_queue_out)
		return 0;

//	/* get the pointer for next empty spot to add at */
//	nwk_packet_t *new_pkt = nwk_send_queue + send_queue_in;

	/* modulus increment the add index */
	send_queue_in = (send_queue_in + 1) % NWK_QUEUE_SIZE;

	/* return the pointer */
	return (nwk_send_queue + send_queue_in);
}

/* External facing function for application layer to send network data */
// simply adds the packet to the network queue if there is space
// returns an appropriate error code if anything goes wrong
uint8_t nwk_send(uint8_t* address, uint8_t* data, uint8_t len)
{
	/* First check all the parameters */
	if(!address)
		return NWK_BAD_ADDRESS;
	if(!data)
		return NWK_BAD_DATA_PTR;
	if(!len || len > 32)
		return NWK_BAD_DATA_LEN;

	xSemaphoreTake(tx_mutex, 0);
	/* Allocate the packet on the queue */
	nwk_packet_t *packet;
	if(!( packet = nwk_tx_pkt_allocate() ))
		return NWK_QUEUE_FULL;

	/* Build the packet */
	memcpy(packet->dest_address, address, NRF24_ADDR_LEN);
	memcpy(packet->data, data, len);
	packet->data_len = len;
	xSemaphoreGive(tx_mutex);

	return NWK_SUCCESS;
}

uint8_t nwk_task(void)
{
	switch(nwk_tx_state)
	{
	case NWK_IDLE:
		if(!nrf24_rf_signal_present()) /* rudimentary CCA */
		{
			if(!nwk_tx_queue_empty())
			{
				nwk_tx_state = NWK_WAITING_FOR_ACK;
				nwk_transmit_pkt();//TODO2: need to handle ack required/not... need to modify pkt types
			}
			while(!(nwk_rcv_queue_empty()))
			{
				nwk_process_pkt();
			}
		}
		else
		{
			printf("RF noise, waiting to send!");
		}
		break;
	case NWK_WAITING_FOR_ACK:
		break;
	default:
		printf("Invalid nwk state!");
		break;
	}

	return NWK_SUCCESS;
}

/* Check if there is anything on the nwk_send_queue */
static bool nwk_tx_queue_empty(void)
{
	return (send_queue_in == send_queue_out);
}

/* Only called during NWK_IDLE, pushes the next item on the send queue out to the chip's "MAC" layer over SPI */
void nwk_transmit_pkt(void)
{

	//TODO2: check if packet needs to be ack'd, if not then remove from queue immediately
	nwk_packet_t tx_pkt = nwk_send_queue[send_queue_out];
//	TODO2: nrf24_set_tx_address(tx_pkt->dest_address);
	nrf24_send(tx_pkt.data, tx_pkt.data_len);
}

/* The callback for transceiver interrupt when a sent packet is either completed or ran out of retries */
void nwk_tx_result_cb(bool completed)
{
	//TODO1: needs to clear the interrupt flags at some point
	if( (completed) && (nwk_tx_state == NWK_WAITING_FOR_ACK) )
	{
		send_queue_out = (send_queue_out + 1) % NWK_QUEUE_SIZE;
		nrf24_setBits_reg(STATUS, _BV(TX_DS));
	}
	else /* the packet hit it's maximum retries */
	{

	}

	nwk_tx_state = NWK_IDLE;

	//TODO2:
	// if((completed && nwk_tx_state == NWK_SENDING) || (retries == 0))
	//		remove from head of queue
	//		if(retries == 0)
	//			remove from transceivers TX FIFO
	//		set state to NWK_IDLE
	// else {
	//		decrement retries
	//		do t
	// }
}


